package yaner.hotel.manager.resource.config;

import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName LogAspectConfig
 * @Description 切面配置
 * @Author leigang
 * @Date 2024/5/22-17:20
 * @Version 1.0
 *
 * 即使用jdk默认代理模式，AspectJ代理模式是CGLIB代理模式
 * 如果目标对象实现了接口，默认情况下会采用JDK的动态代理实现AOP
 * 如果目标对象实现了接口，可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop)
 * 如果目标对象没有实现了接口，必须采用CGLIB库，spring会自动在JDK动态代理和CGLIB之间转换
 */
@Order(1)//使用@Order注解指定切面的优先级，值越小优先级越高。
@Aspect
@EnableAspectJAutoProxy//开启AspectJ 自动代理模式,如果不填proxyTargetClass=true，默认为false，
@Slf4j
@Component
public class LogAspectConfig {
    //定义切入点表达式和切面类，此切入点对应含义为：
    //法一、(execution)选择方法  返回值任意  包名为com.dl.blog.controller 类名任意  方法名任意   参数任意

    /**
     * 法二、@Pointcut("@annotation(* com.dl.blog.config.MyLogger)")
     * MyLogger：自定义注解所在全类名,然后在需要日志的类上加上@MyLogger注解就好了
     */
    @Pointcut("execution(* yaner.hotel.manager.resource.controller.*.*(..))")
    public void aspect() {
    }

    /**
     * 配置前置通知
     *
     * @param joinPoint 切面对象，可用于获取当前被代理类
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String url = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        String className = joinPoint.getTarget().getClass().getName();
        String method = joinPoint.getSignature().getName();
        String classMethod = className + "." + method;
        Object[] args = joinPoint.getArgs();//请求参数
        RequestAspect requestAspect = new RequestAspect(url, ip, classMethod, args);
        log.info("LogAspectConfig before:{}", JSONUtil.toJsonStr(requestAspect));
    }

    /**
     * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
     */
    @After("aspect()")
    public void after() {
//        log.info("------------------doAfter------------------");
    }

    /**
     * 后置异常通知
     *
     * @param joinPoint joinPoint
     */
    @AfterThrowing("aspect()")
    public void throwsHandler(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String method = joinPoint.getSignature().getName();
        String classMethod = className + "." + method;
        log.info("LogAspectConfig throwsHandler:{}方法执行异常！！！", classMethod);
    }

    /**
     * 处理完请求返回内容
     */
    @AfterReturning(returning = "result", pointcut = "aspect()")
    public void doAfterReturn(Object result) {
        log.info("LogAspectConfig doAfterReturn:{}", JSONUtil.toJsonStr(result));
    }

    /**
     * 环绕通知,环绕增强，相当于MethodInterceptor
     *
     * @param  pjp pjp
     * @return
     */
    @Around("aspect()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            Object o = pjp.proceed();
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    //返回结果内部类
    @Data
    private static class RequestAspect {
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;

        public RequestAspect(String url, String ip, String classMethod, Object[] args) {
            this.url = url;
            this.ip = ip;
            this.classMethod = classMethod;
            this.args =args;
        }
    }
}
